package com.luxus.app;

import com.android.camera.MediaSaverImpl;
import com.android.camera.Storage;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.OrientationManagerImpl;
import com.android.camera.async.MainThread;
import com.android.camera.async.Observables;
import com.android.camera.debug.Log;
import com.android.camera.one.CameraId;
import com.android.camera.one.FaceDetectInfo;
import com.android.camera.one.OneCamera.FaceDetectListener;
import com.android.camera.one.OneCamera.Facing;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.session.CaptureSession;
import com.android.camera.session.CaptureSessionFactory;
import com.android.camera.session.CaptureSessionFactoryImpl;
import com.android.camera.session.CaptureSessionManager;
import com.android.camera.session.CaptureSessionManager.SessionListener;
import com.android.camera.session.CaptureSessionManagerImpl;
import com.android.camera.session.PlaceholderManager;
import com.android.camera.session.SessionStorageManager;
import com.android.camera.session.SessionStorageManagerImpl;
import com.android.camera.session.StackSaverFactory;
import com.android.camera.ui.TouchCoordinate;
import com.android.camera.util.Size;
import com.luxus.cdcam.R;
import com.luxus.dev.CameraOperator;
import com.luxus.dev.CameraOperator.CameraConfig;
import com.luxus.dev.GyroOperations;
import com.luxus.dev.ICameraState;
import com.luxus.dev.IGyroListener;
import com.luxus.ui.AutoFitFrameLayout;
import com.luxus.ui.PreviewOverlay;
import com.luxus.ui.ShutterButton;
import com.luxus.ui.ShutterButton.OnShutterButtonListener;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;

public class CDCamActivity extends Activity implements ICameraState, SessionListener {
	private static final Log.CTag TAG = new Log.CTag("CDCamActivity");	
	
	private static final int PERMISSIONS_REQUEST_CAMERA = 1;
    private boolean mPermissionCheckActive = false;
    
    private CameraConfig mMainCamConfig;
	private CameraOperator mMainCamOpt;
	private CameraConfig mSubCamConfig;
	private CameraOperator mSubCamOpt;
	private SurfaceView mSurfaceView;
	private TextureView mTextureView;
	private SurfaceView mAnotherSurfaceView;
	private PreviewOverlay mPreviewOverlay;
	private ViewGroup mVIRoot;
	private FrameLayout mFrontFlash;
	
	private MediaSaver mMediaSaver;
    private CaptureSessionManager mSessionManager;
    private OrientationManagerImpl mOrientationManager;
	    
    private final int FLASH_SOLUTION = 2;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {  
    	Log.d(TAG, "onCreate E");
        super.onCreate(savedInstanceState);
        if (FeatureConfig.useSurfaceType == FeatureConfig.WITH_SURFACEVIEW) {
        	setContentView(R.layout.activity_camera_surfaceview);
        	mSurfaceView = (SurfaceView)findViewById(R.id.preview_display);
        } else if (FeatureConfig.useSurfaceType == FeatureConfig.WITH_TEXTUREVIEW){
        	setContentView(R.layout.activity_camera_textureview);
        	mTextureView = (TextureView)findViewById(R.id.preview_display);
        } else {
        	setContentView(R.layout.activity_camera_nopreview);
        	mSurfaceView = null;
        	mTextureView = null;
        }
        mPreviewOverlay = (PreviewOverlay)findViewById(R.id.preview_overlay);
        AutoFitFrameLayout previewRoot = ((AutoFitFrameLayout)findViewById(R.id.preview_root));
        previewRoot.setAspectRatio(FeatureConfig.usePreviewSize.getHeight(), 
        		FeatureConfig.usePreviewSize.getWidth());
        
        mOrientationManager = new OrientationManagerImpl(this, new Handler(getMainLooper()));
        
        if (!checkPermissions()) return;
        mMainCamConfig = new CameraConfig();
        mMainCamConfig.mCameraId = FeatureConfig.mainCameraId;
        mMainCamConfig.mPreviewRatio = FeatureConfig.usePreviewSize;
        mMainCamConfig.mPictureRatio  = FeatureConfig.usePictureSize;
        mMainCamConfig.mFaceDetectEnable = FeatureConfig.enableFaceDct;
        mMainCamConfig.mFlash = Observables.of(FeatureConfig.useFlashSetting);
        mMainCamConfig.mExposureSetting = Observables.of(0);
        mMainCamConfig.mHdrSceneSetting = Observables.of(false);
        
        if (FeatureConfig.subCameraId != null) {
        	mSubCamConfig = new CameraConfig();
        	mSubCamConfig.mCameraId = FeatureConfig.subCameraId;
        	mSubCamConfig.mPreviewRatio = FeatureConfig.usePreviewSize;
        	mSubCamConfig.mPictureRatio  = FeatureConfig.usePictureSize;
        	mSubCamConfig.mFaceDetectEnable = FeatureConfig.enableFaceDct;
        	mSubCamConfig.mFlash = Observables.of(FeatureConfig.useFlashSetting);
        	mSubCamConfig.mExposureSetting = Observables.of(0);
        	mSubCamConfig.mHdrSceneSetting = Observables.of(false);
        }
        
        Log.d(TAG, "onCreate Open");        
        mMainCamOpt = new CameraOperator(this);
        initMainCamera();
        
        mMediaSaver = new MediaSaverImpl(getContentResolver());
        PlaceholderManager mPlaceHolderManager = new PlaceholderManager(this);
        SessionStorageManager mSessionStorageManager = SessionStorageManagerImpl.create(this);

        StackSaverFactory mStackSaverFactory = new StackSaverFactory(Storage.DIRECTORY,
              getContentResolver());
        CaptureSessionFactory captureSessionFactory = new CaptureSessionFactoryImpl(
                mMediaSaver, mPlaceHolderManager, mSessionStorageManager, mStackSaverFactory);
        mSessionManager = new CaptureSessionManagerImpl(
                captureSessionFactory, mSessionStorageManager, MainThread.create());
        mSessionManager.addSessionListener(this);
        Log.d(TAG, "onCreate X");
    }
    
    protected void onResume() {
    	Log.d(TAG, "onResume E");
    	super.onResume();
    	
    	if (mOrientationManager != null) {
    		mOrientationManager.resume();
    	}
    	getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    	initMainCamera();
    	
    	if (FeatureConfig.useScreenLight == 0) {
    		FeatureConfig.useScreenLight = getCurrentScreenBrightness(getWindow());
    	} else {
    		initializeScreenBrightness(getWindow(), FeatureConfig.useScreenLight);
    	}
    	
    	startGyroDisplay();
    	Log.d(TAG, "onResume X");
    }
    
    private void initializeScreenBrightness(Window window, int level) {
    	WindowManager.LayoutParams params = window.getAttributes();
    	params.screenBrightness = (float)level / 100.0f;
    	window.setAttributes(params);
    }
    
    private int getCurrentScreenBrightness(Window window) {
    	WindowManager.LayoutParams params = window.getAttributes();
    	return (int)(100*params.screenBrightness);
    }
    
    protected void onPause() {
    	super.onPause();
    	
    	mOrientationManager.pause();
    	if (mMainCamOpt != null) {
    		mMainCamOpt.deinit(false);
    	}
    	if (mSubCamOpt != null) {
    		mSubCamOpt.deinit(false);
    	}
    	
    	stopGyroDisplay();
    }
    
    protected void onStop() {
		super.onStop();

		releaseVIContent();
	}
    
    protected void onDestroy() {
    	super.onDestroy();
    	if (mSessionManager != null) {
    		mSessionManager.removeSessionListener(this);
    	}
    	mOrientationManager = null;
    }
    
    private void initMainCamera() {
    	if (mMainCamOpt == null) return;
    	mMainCamOpt.setOrientationManager(mOrientationManager);
        if (mSurfaceView != null) {
    		mMainCamOpt.setSurfaceView(mSurfaceView);
    	} else {
    		mMainCamOpt.setTextureView(mTextureView);
    	}
    	mMainCamOpt.init(mMainCamConfig, this);
    }
    
    private void initSubCamera() {
    	if (mSubCamConfig == null || mSubCamOpt != null) return;
    	mSubCamOpt = new CameraOperator(this);
    	mSubCamOpt.setOrientationManager(mOrientationManager);
    	mSubCamOpt.setSurfaceView(mAnotherSurfaceView);
    	mSubCamOpt.init(mSubCamConfig, this);
    }
    
    @Override
	public void onCameraOpened(CameraId cameraId) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onPreviewStarted(CameraId cameraId) {
		addVIContent();
		if (mSubCamConfig != null && mMainCamConfig.mCameraId.equals(cameraId)) {
			initSubCamera();
		}
	}

	@Override
	public void onPreviewReady(CameraId cameraId) {
		if (mMainCamConfig.mFaceDetectEnable 
				&& mMainCamConfig.mCameraId.equals(cameraId)) {
			mMainCamOpt.setFaceDetectListener(new FaceDetectListener() {
				@Override
				public void onFaceDetectCallback(FaceDetectInfo[] faces) {
					Log.d(TAG, "FaceDetect "+faces.length);
					mPreviewOverlay.showFaceInfo(faces);
				}
			});
		}
	}

	@Override
	public void onCameraClosed(CameraId cameraId) {
		
	}
	
	public void onCapturePreflash(CameraId cameraId) {
		if (FLASH_SOLUTION == 2) {
			startFrontFlash();
		}
	}
	
	@Override
	public void onCaptureCompleted(CameraId cameraId) {
		stopFrontFlash();
	}
	
	private String jpegTitle(long sessionTime) {
		return new String("luxus_"+sessionTime);
	}
	
	private CaptureSession createCaptureSession() {
		long sessionTime = System.currentTimeMillis();
		String title = jpegTitle(sessionTime);
		return mSessionManager.createNewSession(title, sessionTime, null);
	}
	
	private void takePicture() {
		if (mMainCamOpt != null) {
			mMainCamOpt.snap(createCaptureSession());
		}
		if (mSubCamOpt != null) {
			mSubCamOpt.snap(createCaptureSession());
		}
	}
	
	private void enterLongshot() {
		if (mMainCamOpt != null && mSubCamConfig == null) {
			mMainCamOpt.startBurst(new CaptureSession.CaptureSessionCreator() {
                @Override
                public CaptureSession createAndStartEmpty() {
                    CaptureSession session = createCaptureSession();
                    session.startEmpty(null, mMainCamOpt.getCaptureSetting().getPreviewSize());
                    return session;
                }
            });
		}
	}
	
	private void cancelLongshot() {
		if (mMainCamOpt != null && mSubCamConfig == null) {
			mMainCamOpt.stopBurst();
		}
	}
	
	private void addVIContent() {
		if (mVIRoot != null) return;
		mVIRoot = (ViewGroup) findViewById(R.id.control_root);
		ViewGroup vi_content = (ViewGroup)LayoutInflater.from(CDCamActivity.this)
				.inflate(R.layout.activity_vi_content, null);
		mVIRoot.addView(vi_content);
		initVIContent();
	}
	
	private void initVIContent() {
		mFrontFlash = (FrameLayout)mVIRoot.findViewById(R.id.front_flash);
		ShutterButton capture_button = (ShutterButton)mVIRoot.findViewById(R.id.capture_button);
		capture_button.addOnShutterButtonListener(new OnShutterButtonListener() {
			@Override
			public void onShutterButtonFocus(boolean pressed) {
				if (!pressed) {
					cancelLongshot();
				}
			}

			@Override
			public void onShutterCoordinate(TouchCoordinate coord) {
				
			}

			@Override
			public void onShutterButtonClick() {
				if (FLASH_SOLUTION == 1) {
					startFrontFlash();
				}
				takePicture();
			}

			@Override
			public void onShutterButtonLongPressed() {
				enterLongshot();
			}
		});
		if (mSubCamConfig != null) {
			SurfaceView surfaceView = new SurfaceView(this);
			surfaceView.setZOrderOnTop(true);
			mVIRoot.addView((SurfaceView)surfaceView);
			
			FrameLayout.LayoutParams surfaceParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
			surfaceParams.width = 240+120;
			surfaceParams.height= 320+160;
			surfaceParams.gravity = Gravity.TOP | Gravity.CENTER;
			surfaceView.setLayoutParams(surfaceParams);
			surfaceView.setVisibility(View.VISIBLE);
			mAnotherSurfaceView = surfaceView;
		}
	}
	
	private void releaseVIContent() {
		if (mVIRoot != null) {
			mVIRoot.removeAllViews();
			mVIRoot = null;
		}
	}
	    
    private boolean checkPermissions() {
        if (mPermissionCheckActive) return false;

        // Check for all runtime permissions
        if ((checkSelfPermission(Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED )
            || (checkSelfPermission(Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED)
            || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED)) {
            Log.i(TAG, "Requested camera/video permissions");
            requestPermissions(new String[] {
                        Manifest.permission.CAMERA,
                        Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    PERMISSIONS_REQUEST_CAMERA);
            mPermissionCheckActive = true;
            return false;
        }

        return true;
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
            int[] grantResults) {
        mPermissionCheckActive = false;
        if (requestCode == PERMISSIONS_REQUEST_CAMERA) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                    Log.i(TAG, "At least one permission denied, can't continue: " + permissions[i]);
                    finish();
                    return;
                }
            }

            Log.i(TAG, "All permissions granted");
            this.recreate();
        }
    }

	@Override
	public void onSessionQueued(Uri mediaUri) {
		Log.i(TAG, "onSessionQueued mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionUpdated(Uri mediaUri) {
		Log.i(TAG, "onSessionUpdated mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionCaptureIndicatorUpdate(Bitmap bitmap, int rotationDegrees) {
		Log.i(TAG, "onSessionCaptureIndicatorUpdate bitmap = "+bitmap.getWidth()+"x"+bitmap.getHeight());
		Matrix matrix = new Matrix();
        matrix.postRotate(rotationDegrees);
        final Bitmap bitmapIntermediateRotated = Bitmap.createBitmap(
        		bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        
		final ImageView thumbView = (ImageView)mVIRoot.findViewById(R.id.thumbnail_view);
		thumbView.setImageBitmap(bitmapIntermediateRotated);
		Log.i(TAG, "onSessionCaptureIndicatorUpdate rotationDegrees = "+rotationDegrees);
	}

	@Override
	public void onSessionDone(Uri mediaUri) {
		Log.i(TAG, "onSessionDone mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionFailed(Uri mediaUri, int failureMessageId, boolean removeFromFilmstrip) {
		Log.i(TAG, "onSessionFailed mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionCanceled(Uri mediaUri) {
		Log.i(TAG, "onSessionCanceled mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionProgress(Uri mediaUri, int progress) {
		Log.i(TAG, "onSessionProgress mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionProgressText(Uri mediaUri, int messageId) {
		Log.i(TAG, "onSessionProgressText mediaUri = "+mediaUri);
	}

	@Override
	public void onSessionThumbnailUpdate(Bitmap bitmap) {
		Log.i(TAG, "onSessionThumbnailUpdate bitmap = "+bitmap.getWidth()+"x"+bitmap.getHeight());
	}

	@Override
	public void onSessionPictureDataUpdate(byte[] pictureData, int orientation) {
		Log.i(TAG, "onSessionPictureDataUpdate");
	}
	
	private GyroOperations mGyroOperations;
	
	private void startGyroDisplay() {
		if (FeatureConfig.gyroMode == GyroOperations.GYRO_NONE) {
			return;
		}
		OneCameraCharacteristics cameraCharacteristics = mMainCamOpt.getCharacteristics();
		float[] fovs = cameraCharacteristics.getFieldOfView();
        mPreviewOverlay.setFieldOfView(fovs[0], fovs[1]);
        mPreviewOverlay.setFacingAndOrientation(cameraCharacteristics.getCameraDirection() , 
        		cameraCharacteristics.getSensorOrientation());
		if (mGyroOperations == null) {
            SensorManager sensorManager = (SensorManager) getSystemService(this.SENSOR_SERVICE);
            mGyroOperations = new GyroOperations(sensorManager);
        }
        mGyroOperations.startListening(
                new IGyroListener() {
                    @Override
                    public void updateGyroAngles(float[] gyroAngles) {
                        mPreviewOverlay.setGyroAngles(gyroAngles);
                    }
                }, FeatureConfig.gyroMode);

        mPreviewOverlay.showGyroGrid(true);
	}
	
	private void stopGyroDisplay() {
        if (mGyroOperations != null) {
            mGyroOperations.stopListening();
        }
        mPreviewOverlay.showGyroGrid(false);
    }
	
	private void startFrontFlash() {
		if ("off".equals(FeatureConfig.useFlashSetting)) return;
		if (mMainCamOpt.getCharacteristics().getCameraDirection() != Facing.FRONT) return;
		Log.i(TAG, "startFrontFlash");
		initializeScreenBrightness(getWindow(), 100);
		mFrontFlash.setBackgroundColor(Color.rgb(255, 255, 220));
		mFrontFlash.setVisibility(View.VISIBLE);
	}
	
	private void stopFrontFlash() {
		if ("off".equals(FeatureConfig.useFlashSetting)) return;
		if (mMainCamOpt.getCharacteristics().getCameraDirection() != Facing.FRONT) return;
		Log.i(TAG, "stopFrontFlash");
		initializeScreenBrightness(getWindow(), FeatureConfig.useScreenLight);
		mFrontFlash.setVisibility(View.GONE);
	}
}
